1 /* 2 * Copyright (C) 2010 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.collect; 16 17 import static com.google.common.base.Preconditions.checkNotNull; 18 19 import com.google.common.annotations.Beta; 20 import com.google.common.annotations.GwtCompatible; 21 import com.google.common.base.Function; 22 23 import java.util.Collections; 24 import java.util.Comparator; 25 import java.util.List; 26 import java.util.RandomAccess; 27 28 import javax.annotation.Nullable; 29 30 /** 31 * Static methods pertaining to sorted {@link List} instances. 32 * 33 * In this documentation, the terms <i>greatest</i>, <i>greater</i>, <i>least</i>, and 34 * <i>lesser</i> are considered to refer to the comparator on the elements, and the terms 35 * <i>first</i> and <i>last</i> are considered to refer to the elements' ordering in a 36 * list. 37 * 38 * @author Louis Wasserman 39 */ 40 @GwtCompatible 41 @Beta final class SortedLists { 42 private SortedLists() {} 43 44 /** 45 * A specification for which index to return if the list contains at least one element that 46 * compares as equal to the key. 47 */ 48 public enum KeyPresentBehavior { 49 /** 50 * Return the index of any list element that compares as equal to the key. No guarantees are 51 * made as to which index is returned, if more than one element compares as equal to the key. 52 */ 53 ANY_PRESENT { 54 @Override 55 <E> int resultIndex( 56 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) { 57 return foundIndex; 58 } 59 }, 60 /** 61 * Return the index of the last list element that compares as equal to the key. 62 */ 63 LAST_PRESENT { 64 @Override 65 <E> int resultIndex( 66 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) { 67 // Of course, we have to use binary search to find the precise 68 // breakpoint... 69 int lower = foundIndex; 70 int upper = list.size() - 1; 71 // Everything between lower and upper inclusive compares at >= 0. 72 while (lower < upper) { 73 int middle = (lower + upper + 1) >>> 1; 74 int c = comparator.compare(list.get(middle), key); 75 if (c > 0) { 76 upper = middle - 1; 77 } else { // c == 0 78 lower = middle; 79 } 80 } 81 return lower; 82 } 83 }, 84 /** 85 * Return the index of the first list element that compares as equal to the key. 86 */ 87 FIRST_PRESENT { 88 @Override 89 <E> int resultIndex( 90 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) { 91 // Of course, we have to use binary search to find the precise 92 // breakpoint... 93 int lower = 0; 94 int upper = foundIndex; 95 // Of course, we have to use binary search to find the precise breakpoint... 96 // Everything between lower and upper inclusive compares at <= 0. 97 while (lower < upper) { 98 int middle = (lower + upper) >>> 1; 99 int c = comparator.compare(list.get(middle), key); 100 if (c < 0) { 101 lower = middle + 1; 102 } else { // c == 0 103 upper = middle; 104 } 105 } 106 return lower; 107 } 108 }, 109 /** 110 * Return the index of the first list element that compares as greater than the key, or {@code 111 * list.size()} if there is no such element. 112 */ 113 FIRST_AFTER { 114 @Override 115 public <E> int resultIndex( 116 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) { 117 return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1; 118 } 119 }, 120 /** 121 * Return the index of the last list element that compares as less than the key, or {@code -1} 122 * if there is no such element. 123 */ 124 LAST_BEFORE { 125 @Override 126 public <E> int resultIndex( 127 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) { 128 return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1; 129 } 130 }; 131 abstract <E> int resultIndex( 132 Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex); 133 } 134 135 /** 136 * A specification for which index to return if the list contains no elements that compare as 137 * equal to the key. 138 */ 139 public enum KeyAbsentBehavior { 140 /** 141 * Return the index of the next lower element in the list, or {@code -1} if there is no such 142 * element. 143 */ 144 NEXT_LOWER { 145 @Override 146 int resultIndex(int higherIndex) { 147 return higherIndex - 1; 148 } 149 }, 150 /** 151 * Return the index of the next higher element in the list, or {@code list.size()} if there is 152 * no such element. 153 */ 154 NEXT_HIGHER { 155 @Override 156 public int resultIndex(int higherIndex) { 157 return higherIndex; 158 } 159 }, 160 /** 161 * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at 162 * which the key would be inserted into the list: the index of the next higher element in the 163 * list, or {@code list.size()} if there is no such element. 164 * 165 * <p>Note that the return value will be {@code >= 0} if and only if there is an element of the 166 * list that compares as equal to the key. 167 * 168 * <p>This is equivalent to the behavior of 169 * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't present, since 170 * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}. 171 */ 172 INVERTED_INSERTION_INDEX { 173 @Override 174 public int resultIndex(int higherIndex) { 175 return ~higherIndex; 176 } 177 }; 178 179 abstract int resultIndex(int higherIndex); 180 } 181 182 /** 183 * Searches the specified naturally ordered list for the specified object using the binary search 184 * algorithm. 185 * 186 * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, 187 * KeyAbsentBehavior)} using {@link Ordering#natural}. 188 */ 189 public static <E extends Comparable> int binarySearch(List<? extends E> list, E e, 190 KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { 191 checkNotNull(e); 192 return binarySearch( 193 list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior); 194 } 195 196 /** 197 * Binary searches the list for the specified key, using the specified key function. 198 * 199 * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, 200 * KeyAbsentBehavior)} using {@link Ordering#natural}. 201 */ 202 public static <E, K extends Comparable> int binarySearch(List<E> list, 203 Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior, 204 KeyAbsentBehavior absentBehavior) { 205 return binarySearch( 206 list, 207 keyFunction, 208 key, 209 Ordering.natural(), 210 presentBehavior, 211 absentBehavior); 212 } 213 214 /** 215 * Binary searches the list for the specified key, using the specified key function. 216 * 217 * <p>Equivalent to 218 * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} using 219 * {@link Lists#transform(List, Function) Lists.transform(list, keyFunction)}. 220 */ 221 public static <E, K> int binarySearch( 222 List<E> list, 223 Function<? super E, K> keyFunction, 224 @Nullable K key, 225 Comparator<? super K> keyComparator, 226 KeyPresentBehavior presentBehavior, 227 KeyAbsentBehavior absentBehavior) { 228 return binarySearch( 229 Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); 230 } 231 232 /** 233 * Searches the specified list for the specified object using the binary search algorithm. The 234 * list must be sorted into ascending order according to the specified comparator (as by the 235 * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior 236 * to making this call. If it is not sorted, the results are undefined. 237 * 238 * <p>If there are elements in the list which compare as equal to the key, the choice of 239 * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to 240 * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned. 241 * 242 * <p>This method runs in log(n) time on random-access lists, which offer near-constant-time 243 * access to each list element. 244 * 245 * @param list the list to be searched. 246 * @param key the value to be searched for. 247 * @param comparator the comparator by which the list is ordered. 248 * @param presentBehavior the specification for what to do if at least one element of the list 249 * compares as equal to the key. 250 * @param absentBehavior the specification for what to do if no elements of the list compare as 251 * equal to the key. 252 * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list; 253 * otherwise the index determined by the {@code KeyAbsentBehavior}. 254 */ 255 public static <E> int binarySearch(List<? extends E> list, @Nullable E key, 256 Comparator<? super E> comparator, KeyPresentBehavior presentBehavior, 257 KeyAbsentBehavior absentBehavior) { 258 checkNotNull(comparator); 259 checkNotNull(list); 260 checkNotNull(presentBehavior); 261 checkNotNull(absentBehavior); 262 if (!(list instanceof RandomAccess)) { 263 list = Lists.newArrayList(list); 264 } 265 // TODO(user): benchmark when it's best to do a linear search 266 267 int lower = 0; 268 int upper = list.size() - 1; 269 270 while (lower <= upper) { 271 int middle = (lower + upper) >>> 1; 272 int c = comparator.compare(key, list.get(middle)); 273 if (c < 0) { 274 upper = middle - 1; 275 } else if (c > 0) { 276 lower = middle + 1; 277 } else { 278 return lower + presentBehavior.resultIndex( 279 comparator, key, list.subList(lower, upper + 1), middle - lower); 280 } 281 } 282 return absentBehavior.resultIndex(lower); 283 } 284 }